
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import java.util.*;

class Game extends JFrame implements KeyListener
{
	java.util.List<Entity> entities = new ArrayList<Entity>();
	java.util.List<Node> nodes = new ArrayList<Node>();	
	java.util.List<Gun> weaponlist = new ArrayList<Gun>();
	
	Player player;
	boolean up,down,left,right,fire;
	int nodedelay = 0;
	Node a, b, c, d, e, f, g, h, i, j, k;
	
	public void setup()
	{
		//top
		entities.add(new Wall( 13, 34, "Map/topleft.png" )); 
		entities.add(new Wall( 126, 31, "Map/top.png" )); 
		entities.add(new Wall( 252, 34, "Map/topdoor.png" )); 
		entities.add(new Wall( 408, 34, "Map/topdoor.png" )); 
		entities.add(new Wall( 536, 31, "Map/top.png" ));
		entities.add(new Wall( 642, 34, "Map/topright.png" )); 
		//bottom 600 middle
		entities.add(new Wall( 18, 629, "Map/bottomleft.png" )); 
		entities.add(new Wall( 131, 641, "Map/bottom.png" ));
		entities.add(new Wall( 258, 628, "Map/bottomdoor.png" ));
		entities.add(new Wall( 400, 628, "Map/bottomdoor.png" ));
		entities.add(new Wall( 528, 641, "Map/bottom.png" ));
		entities.add(new Wall( 640, 629, "Map/bottomright.png" ));
		//sides
		entities.add(new Wall( -6, 324, "Map/right.png" ));
		entities.add(new Wall( 649, 324, "Map/left.png" ));
		
		//nodes
		nodes.add( a = new Node( 110.0, 161.0, "a"));
		nodes.add( b = new Node( 330.0, 161.0, "b"));	
		nodes.add( c = new Node( 550.0, 161.0, "c"));
		nodes.add( d = new Node( 110.0, 339.0, "d"));
		nodes.add( e = new Node( 330.0, 339.0, "e"));	
		nodes.add( f = new Node( 550.0, 339.0, "f"));
		nodes.add( g = new Node( 110.0, 517.0, "g"));
		nodes.add( h = new Node( 330.0, 517.0, "h"));	
		nodes.add( i = new Node( 550.0, 517.0, "i"));
		nodes.add( j = new Node( 330.0,-161.0, "j"));
		nodes.add( k = new Node( 330.0, 978.0, "k"));
		
		a.addEdge(b); a.addEdge(d);
		b.addEdge(a); b.addEdge(j); b.addEdge(c); b.addEdge(e);
		c.addEdge(b); c.addEdge(f); 
		d.addEdge(a); d.addEdge(e); d.addEdge(g);
		e.addEdge(b); e.addEdge(f); e.addEdge(h); e.addEdge(d);
		f.addEdge(c); f.addEdge(e); f.addEdge(i);
		g.addEdge(d); g.addEdge(h);
		h.addEdge(g); h.addEdge(e); h.addEdge(i); h.addEdge(k);
		i.addEdge(f); i.addEdge(d);
		j.addEdge(b);
		k.addEdge(k);
		
		player = new Player();
		entities.add(player);
		
		Zombie e = new Zombie();
		e.x = 200;
		entities.add(e);		
		
		e = new Zombie();
		e.x = 400;
		entities.add(e);
		
		e = new Zombie();
		e.x = 500;
		entities.add(e);
		
		e = new Zombie();
		e.x = 300;
		entities.add(e);		

	}
	
	public Node nearestNode(double nx, double ny)
	{
		
		Node closestNode = null;
		double closestDistance = Double.MAX_VALUE;
		
		for ( Node n : nodes )
		{
			double dx = nx - n.x;
			double dy = ny - n.y;
			double d =  Math.sqrt ( (dx * dx) + (dy * dy) );
			if ( d < closestDistance)
			{
				closestNode = n;
				closestDistance = d;
			}
		}
		
		return closestNode;
	}
	
	enum Direction
	{
		NW(-1,-1),
		N(0,-1),
		NE(1,-1),
		E(1,0),
		SE(1,1),
		S(0,1),
		SW(-1,1),
		W(-1,0);
		double x,y,theta;
		private Direction(double x, double y){
			this.x=x;
			this.y=y;
			this.theta=Math.atan2(y,x);
			while(theta >= Math.PI * 2) theta -= Math.PI*2;
			while(theta < 0) theta += Math.PI*2;
		}
		static Direction forAngle(double theta)
		{
			theta += Math.PI/8;
			while(theta >= Math.PI * 2) theta -= Math.PI*2;
			while(theta < 0) theta += Math.PI*2;
			int n = (int)Math.floor ( ( theta / (2 * Math.PI) ) * 8 );
			switch(n){
				case 0 : return E;
				case 1 : return SE;
				case 2 : return S;
				case 3 : return SW;
				case 4 : return W;
				case 5 : return NW;
				case 6 : return N;
				case 7 : return NE;
			}
			return null;
		}
	}
	class Entity
	{
		Image image;
		double w = 25;
		double iw = 50;
		double h = 25;
		double ih = 50;
		double x = 100;
		double y = 100;
		double angle = 0;
		double health = 90;
		
		public void paint(Graphics2D g)
		{	
			AffineTransform trans = new AffineTransform();
			trans.translate(x,y);		//transforms applied in opposite order
			trans.rotate(angle);
			trans.translate(-iw/2,-ih/2);
			
			g.drawImage(image, trans, null);
		//	g.setColor(Color.GREEN);
		//	g.drawRect((int)(x-w/2),(int)(y-h/2),(int)(w),(int)(h));
			
			
			g.setColor(Color.RED);
			g.fillRect((int)(x-50),(int)(y-30),100,10);
			g.setColor(Color.GREEN);
			g.fillRect((int)(x-50),(int)(y-30),(int)(health),10);
		}
		public void step()
		{
		}
		public boolean moveIfNoIntersect(double dx, double dy)
		{
			double oldx = x;
			double oldy = y;
			x+=dx;
			y+=dy;
			for(Entity e: entities)
			{
				if(e == this) continue;
				if(e instanceof Animation) continue;
				if(e instanceof Bullet) continue;
				
				if(intersects(e)){
					x=oldx;
					y=oldy;
					if (this instanceof Zombie) {
						((Zombie)this).upa.turnOff();
						((Zombie)this).uprighta.turnOff();
						((Zombie)this).righta.turnOff();
						((Zombie)this).downrighta.turnOff();
						((Zombie)this).downlefta.turnOff();
						((Zombie)this).downa.turnOff();
						((Zombie)this).lefta.turnOff();
						((Zombie)this).uplefta.turnOff();
						((Zombie)this).hitdelay = 15;
					}
					return false;
				}
			}
			return true;
		}
		public boolean intersects(Entity e)
		{
			double dx = Math.abs( x - e.x );
			double dy = Math.abs( y - e.y );
			return dx < (w+e.w)/2 && dy < (h+e.h)/2;
		}
	}
	
	class Node 
	{
		public final double x, y;
		public final String name;
		public final Collection edges = new ArrayList(); 
		
		public Node(double x, double y, String name)
		{
			this.x = x;
			this.y = y;
			this.name = name;
		}
		
		public Node addEdge(Node n)
		{
			edges.add(n);
			return this;
		}
		public Collection<Node> edges()
		{ 
			return edges;
		}
		
		
	}
	

	
	class Wall extends Entity 
	{
		public Wall ( double x, double y, String filename)
		{
			this.x = x;
			this.y = y;
			ImageIcon ii = new ImageIcon(filename);
			w = iw = ii.getIconWidth();
			h = ih = ii.getIconHeight(); 
			image = ii.getImage(); 
		}
		
		public void paint(Graphics2D g)	{	
			AffineTransform trans = new AffineTransform();
			trans.translate(x,y);		//transforms applied in opposite order
			trans.rotate(angle);
			trans.translate(-iw/2,-ih/2);
			
			g.drawImage(image, trans, null);
		}
	}
	
	class Gun
	{
		int damage;
		int velocity;
		int ammo;
	}
	
	class Bullet extends Entity
	{
		double vx, vy;
		int ttl = 60*3;
		Bullet(double x, double y, double vx, double vy)
		{
			this.x=x;
			this.y=y;
			this.vx=vx;
			this.vy=vy;
			
			this.w=0;
			this.h=0;
		}
		public void step()
		{
			x+=vx;
			y+=vy;
			ttl--;
			if(ttl==0) entities.remove(this);
			
			for(Entity e: entities)
			{
				if(e instanceof Player || e instanceof Zombie){
					if(intersects(e)){
						e.health -= 34;
						e.moveIfNoIntersect(vx * 1.25, vy * 1.25);
						if (e instanceof Zombie ) ((Zombie)e).hitdelay = 6;
						entities.remove(this);
						return;
					}
				} else if(e instanceof Wall) {
					if(intersects(e)){
						entities.remove(this);
						return;
					}
				} 
				
			}
			if(this.x > 649 || this.x < 0) {entities.remove(this);}
			if(this.y < 0 || this.y > 661) {entities.remove(this);}
		}
		public void paint(Graphics2D g)
		{
			g.setColor(Color.BLACK);
			g.fillOval( (int)(x-3), (int)(y-3), 6, 6);
		}
	}
	class Player extends Entity 
	{
		ToggleLoopAnimation lefta, righta, upa, downa, uprighta, uplefta, downrighta, downlefta;
		int firedelay = 0;
		Direction d = Direction.E;
		
		Player()
		{
			image = imageFromFile("Player/down-m.PNG");
			int r = 6; //refresh rate
			
			lefta = new ToggleLoopAnimation(this);
			lefta.addTransition(1 * r,  imageFromFile("Player/left-r.PNG") );
			lefta.addTransition(2 * r, imageFromFile("Player/left-m.PNG") );
			lefta.addTransition(3 * r, imageFromFile("Player/left-l.PNG") );
			lefta.addTransition(4 * r, imageFromFile("Player/left-m.PNG") );
			entities.add(lefta);
			
			righta = new ToggleLoopAnimation(this);
			righta.addTransition(1 * r,  imageFromFile("Player/right-l.PNG") );
			righta.addTransition(2 * r, imageFromFile("Player/right-m.PNG") );
			righta.addTransition(3 * r, imageFromFile("Player/right-r.PNG") );
			righta.addTransition(4 * r, imageFromFile("Player/right-m.PNG") );
			entities.add(righta);
			
			upa = new ToggleLoopAnimation(this);
			upa.addTransition(1 * r,  imageFromFile("Player/up-r.PNG") );
			upa.addTransition(2 * r, imageFromFile("Player/up-m.PNG") );
			upa.addTransition(3 * r, imageFromFile("Player/up-l.PNG") );
			upa.addTransition(4 * r, imageFromFile("Player/up-m.PNG") );
			entities.add(upa);
			
			downa = new ToggleLoopAnimation(this);
			downa.addTransition(1 * r,  imageFromFile("Player/down-r.PNG") );
			downa.addTransition(2 * r, imageFromFile("Player/down-m.PNG") );
			downa.addTransition(3 * r, imageFromFile("Player/down-l.PNG") );
			downa.addTransition(4 * r, imageFromFile("Player/down-m.PNG") );
			entities.add(downa);
			
			uprighta = new ToggleLoopAnimation(this);
			uprighta.addTransition(1 * r, imageFromFile("Player/upright-l.PNG") );
			uprighta.addTransition(2 * r, imageFromFile("Player/upright-m.PNG") );
			uprighta.addTransition(3 * r, imageFromFile("Player/upright-r.PNG") );
			uprighta.addTransition(4 * r, imageFromFile("Player/upright-m.PNG") );
			entities.add(uprighta);
			
			uplefta = new ToggleLoopAnimation(this);
			uplefta.addTransition(1 * r, imageFromFile("Player/upleft-r.PNG") );
			uplefta.addTransition(2 * r, imageFromFile("Player/upleft-m.PNG") );
			uplefta.addTransition(3 * r, imageFromFile("Player/upleft-l.PNG") );
			uplefta.addTransition(4 * r, imageFromFile("Player/upleft-m.PNG") );
			entities.add(uplefta);
			
			downrighta = new ToggleLoopAnimation(this);
			downrighta.addTransition(1 * r, imageFromFile("Player/downright-l.PNG") );
			downrighta.addTransition(2 * r, imageFromFile("Player/downright-m.PNG") );
			downrighta.addTransition(3 * r, imageFromFile("Player/downright-r.PNG") );
			downrighta.addTransition(4 * r, imageFromFile("Player/downright-m.PNG") );
			entities.add(downrighta);
			
			downlefta = new ToggleLoopAnimation(this);
			downlefta.addTransition(1 * r, imageFromFile("Player/downleft-r.PNG") );
			downlefta.addTransition(2 * r, imageFromFile("Player/downleft-m.PNG") );
			downlefta.addTransition(3 * r, imageFromFile("Player/downleft-l.PNG") );
			downlefta.addTransition(4 * r, imageFromFile("Player/downleft-m.PNG") );
			entities.add(downlefta);
		}
		public void step()
		{
			int i = 4;
			double s = Math.sqrt(2);
			
			if(fire && firedelay < 0){
				double z = Math.max(w,h)+4;
				entities.add(new Bullet(x+d.x*z,y+d.y*z,12*d.x,12*d.y));
				firedelay = 15;
			}			
			firedelay--;
			
			upa.turnOff();lefta.turnOff();righta.turnOff();downa.turnOff();
			uprighta.turnOff();downrighta.turnOff();downlefta.turnOff();uplefta.turnOff();
				
			if(up && left) {
				moveIfNoIntersect(-i/s, -i/s);
				uplefta.turnOn();
				d = Direction.NW;
			} else if(up && right) {
				moveIfNoIntersect(i/s, -i/s);
				uprighta.turnOn();
				d = Direction.NE;
			} else if(down && right) {
				moveIfNoIntersect(i/s, i/s);
				downrighta.turnOn();
				d = Direction.SE;
			} else if(down && left) {
				moveIfNoIntersect(-i/s, i/s);
				downlefta.turnOn();
				d = Direction.SW;
			} else if(up) {
				moveIfNoIntersect(0.0, -i);
				upa.turnOn();
				d = Direction.N;
			} else if(down) {
				moveIfNoIntersect(0.0, i);
				downa.turnOn();
				d = Direction.S;
			} else if(right) {
				moveIfNoIntersect(i, 0.0);
				righta.turnOn();
				d = Direction.E;
			} else if(left) {
				moveIfNoIntersect(-i, 0.0);
				lefta.turnOn();
				d = Direction.W;
			} else {
			}
		}
		void switchTo(Animation a)
		{
		}
	}
	class Zombie extends Entity
	{
		java.util.List<Node> openlist = new ArrayList<Node>();	// holder
		java.util.List<Node> closedlist = new ArrayList<Node>(); //directions
		
		ToggleLoopAnimation lefta, righta, upa, downa, uprighta, uplefta, downrighta, downlefta;
		Direction d = Direction.W;
		double speed =	-1; 
		int hitdelay = 0;		
		
		Zombie()
		{
			image = imageFromFile("Zombie/down-m.PNG");
			int r = 6; //refresh rate
		
			lefta = new ToggleLoopAnimation(this);
			lefta.addTransition(1 * r, imageFromFile("Zombie/left-r.PNG") );
			lefta.addTransition(2 * r, imageFromFile("Zombie/left-m.PNG") );
			lefta.addTransition(3 * r, imageFromFile("Zombie/left-l.PNG") );
			lefta.addTransition(4 * r, imageFromFile("Zombie/left-m.PNG") );
			entities.add(lefta);
			
			righta = new ToggleLoopAnimation(this);
			righta.addTransition(1 * r, imageFromFile("Zombie/right-l.PNG") );
			righta.addTransition(2 * r, imageFromFile("Zombie/right-m.PNG") );
			righta.addTransition(3 * r, imageFromFile("Zombie/right-r.PNG") );
			righta.addTransition(4 * r, imageFromFile("Zombie/right-m.PNG") );
			entities.add(righta);
			
			upa = new ToggleLoopAnimation(this);
			upa.addTransition(1 * r, imageFromFile("Zombie/up-r.PNG") );
			upa.addTransition(2 * r, imageFromFile("Zombie/up-m.PNG") );
			upa.addTransition(3 * r, imageFromFile("Zombie/up-l.PNG") );
			upa.addTransition(4 * r, imageFromFile("Zombie/up-m.PNG") );
			entities.add(upa);
			
			downa = new ToggleLoopAnimation(this);
			downa.addTransition(1 * r, imageFromFile("Zombie/down-r.PNG") );
			downa.addTransition(2 * r, imageFromFile("Zombie/down-m.PNG") );
			downa.addTransition(3 * r, imageFromFile("Zombie/down-l.PNG") );
			downa.addTransition(4 * r, imageFromFile("Zombie/down-m.PNG") );
			entities.add(downa);
			
			uprighta = new ToggleLoopAnimation(this);
			uprighta.addTransition(1 * r, imageFromFile("Zombie/upright-l.PNG") );
			uprighta.addTransition(2 * r, imageFromFile("Zombie/upright-m.PNG") );
			uprighta.addTransition(3 * r, imageFromFile("Zombie/upright-r.PNG") );
			uprighta.addTransition(4 * r, imageFromFile("Zombie/upright-m.PNG") );
			entities.add(uprighta);;
			
			uplefta = new ToggleLoopAnimation(this);
			uplefta.addTransition(1 * r, imageFromFile("Zombie/upleft-r.PNG") );
			uplefta.addTransition(2 * r, imageFromFile("Zombie/upleft-m.PNG") );
			uplefta.addTransition(3 * r, imageFromFile("Zombie/upleft-l.PNG") );
			uplefta.addTransition(4 * r, imageFromFile("Zombie/upleft-m.PNG") );
			entities.add(uplefta);
			
			downrighta = new ToggleLoopAnimation(this);
			downrighta.addTransition(1 * r, imageFromFile("Zombie/downright-l.PNG") );
			downrighta.addTransition(2 * r, imageFromFile("Zombie/downright-m.PNG") );
			downrighta.addTransition(3 * r, imageFromFile("Zombie/downright-r.PNG") );
			downrighta.addTransition(4 * r, imageFromFile("Zombie/downright-m.PNG") );
			entities.add(downrighta);
			
			downlefta = new ToggleLoopAnimation(this);
			downlefta.addTransition(1 * r, imageFromFile("Zombie/downleft-r.PNG") );
			downlefta.addTransition(2 * r, imageFromFile("Zombie/downleft-m.PNG") );
			downlefta.addTransition(3 * r, imageFromFile("Zombie/downleft-l.PNG") );
			downlefta.addTransition(4 * r, imageFromFile("Zombie/downleft-m.PNG") );
			entities.add(downlefta);
		}
		public void step()
		{	
		

			
			if(health < 0)
			{
				entities.remove(this);
				
				Zombie z = new Zombie();
				z.x = player.x + 500*Math.sin(Math.random()*Math.PI*2);
				z.y = player.y + 500*Math.cos(Math.random()*Math.PI*2);
				entities.add(z);
				return;
			}
			
			if (hitdelay > 0) {
				upa.turnOff();
				uprighta.turnOff();
				righta.turnOff();
				downrighta.turnOff();
				downlefta.turnOff();
				downa.turnOff();
				lefta.turnOff();
				uplefta.turnOff();
				hitdelay--;
				return;
			}

			
			Node playerNode = nearestNode(player.x, player.y);
			Node zombieNode = nearestNode(this.x, this.y);
			
			if ( zombieNode == null || playerNode == null) { System.out.println("A node is null"); }
			
			Node nextNode = null;
			double tempDistance = 100000;
			
			if( playerNode == zombieNode)  // If we're close enough to the player, just go straight at him
				{
					//System.out.println("close enough ");
					d = Direction.forAngle(Math.atan2(player.y - y, player.x - x));		// Find direction to player and go
				} 
			else  // If we're not close enough to the player, follow directions in closedlist
				{
					if (nodedelay < 0 || this.closedlist.size() == 0)  // If it is time to refresh route or the route is empty
					{						
							this.closedlist.clear();				// Clear the old route
							this.closedlist.add(zombieNode);		// Add the starting location
										
							//System.out.println("\n\nRefreshing path.");
										
							while ( this.closedlist.get( this.closedlist.size() - 1) != playerNode)	// While our directions have not gotten us to the player
							{		

								//System.out.println("Check node: " + this.closedlist.get( this.closedlist.size() - 1).name);
							
								for ( Node z : this.closedlist.get( this.closedlist.size() - 1).edges() )	// Check all the edges
								{					
									
									double dx = playerNode.x - z.x;
									double dy = playerNode.y - z.y;
									double d =  Math.sqrt ( (dx * dx) + (dy * dy) );	// Find the distance to the player's node
									
									if ( d < tempDistance)	// If the new distance is less than a different edge
									{
										nextNode = z;	// Set the next node to the shorter
										tempDistance = d;	// Set the distance to beat
									}
								}
								
								//System.out.println("Adding node: " + nextNode.name);
								
								this.closedlist.add(nextNode); 	// After going through the edges, add the best node to the direction list 
								tempDistance = 100000;
							}
							
							
							
							nodedelay = 60; // Reset the node delay
					}
				
					if (this.closedlist.get(0) == zombieNode) // If we're at the first direction already, 
					{						
						this.closedlist.remove(0);	// Delete the direction
						
						if ( this.closedlist.size() == 0) {
							// Shouldn't happen
							//System.out.println("size is zero");
							
						}
						else {
							d = Direction.forAngle(Math.atan2(this.closedlist.get(0).y - y, this.closedlist.get(0).x - x));	// Move onto the next direction						
						}
					}
					else if (this.closedlist.get(0) != zombieNode)	// If we're not at the first direction,
					{						
						d = Direction.forAngle(Math.atan2(this.closedlist.get(0).y - y, this.closedlist.get(0).x - x)); // Go towards the first direction		
					}
				}
				

			nodedelay--; // Move the delay down
			
			
			speed = 1 + (health-100)/200;			
			moveIfNoIntersect( speed * d.x, speed * d.y );
			
			upa.turnOff();lefta.turnOff();downa.turnOff();uprighta.turnOff();
			downrighta.turnOff();downlefta.turnOff();uplefta.turnOff();righta.turnOff();
			
			switch(d){
				case N: upa.turnOn(); break;
				case NE: uprighta.turnOn(); break;
				case E: righta.turnOn(); break;
				case SE: downrighta.turnOn(); break;
				case S: downa.turnOn(); break;
				case SW: downlefta.turnOn(); break;
				case W: lefta.turnOn(); break;
				case NW: uplefta.turnOn(); break;
			}
			
		}
		
		public void paint(Graphics2D g)
		{
			super.paint(g);
			g.setColor(Color.BLUE);
			
			g.drawLine( (int)(x), (int)(y), (int)(x+d.x*30), (int)(y + d.y*30) );
		}
	}
	
	//////////////////////////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////////////////////
	
	class Animation extends Entity
	{
		class Transition
		{
			int frame;
			Image image;
		}
		
		java.util.List<Transition> transitions = new LinkedList<Transition>();
		int frame=0;
		Entity subject;
		
		Animation(Entity subject)
		{
			this.subject = subject;
		}

		public void addTransition(int frame, Image newImage)
		{
			Transition t = new Transition();
			t.frame = frame;
			t.image = newImage;
			transitions.add(t);
		}
		
		public void step()
		{
			if(transitions.size()==0) {
				//done!
				entities.remove(this); 
				return;
			}
			
			Transition t = transitions.get(0);
			if(t.frame == frame)
			{
				subject.image = t.image;
				transitions.remove(t);
			}
			
			frame++;
		}
		public void paint(Graphics2D g)
		{
		}
	}
	class ToggleLoopAnimation extends Animation
	{
		boolean wason = false;
		boolean on = false;
		int index = 0;
		
		public ToggleLoopAnimation(Entity subject)
		{
			super(subject);
		}
		public void turnOn()
		{
			on = true;
		}
		public void turnOff()
		{			
			on = false;
		}
		public void step()
		{
			if( !on && wason ) {
				subject.image = transitions.get(transitions.size()-1).image;
				wason = false;
				return;
			}
			if( !on ) return;
			
			Transition t = transitions.get(index);
			if(t.frame == frame)
			{
				subject.image = t.image;
				index = (index + 1);

				if( index == transitions.size() )
				{
					index = 0;
					frame = -1;
				}
			}
			
			frame++;
			wason = true;
		}
	}
	
	public Game()
	{
		super("SphereHead");
		
		setup();
		
		addKeyListener(this);
		this.setSize(670,690);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setVisible(true);
		
		JComponent pane = new JComponent(){
			public void paint(Graphics g){
				gamePaint(g);
			}
		};
		pane.setDoubleBuffered(true);
		add(pane);
		
		new Thread(){
			public void run(){
				mainLoop();
			}
		}.start();
	}
	
	public void mainLoop()
	{
		while(true)
		{
			try{Thread.sleep(1000/60);}catch(Exception ex){}
			
			for(Entity e: new ArrayList<Entity>(entities))
				e.step();
			
			repaint();
		}
	}
	
	public void gamePaint(Graphics g1)
	{
		Graphics2D g = (Graphics2D) g1;
		
		g.setColor(Color.WHITE);
		g.fillRect(0,0,getWidth(),getHeight());
		
		for(Entity e: entities)
			e.paint(g);
		
		/*
		Map<Node,Color> m = new HashMap<Node,Color>();
		m.put(a, Color.RED);
		m.put(b, Color.BLUE);
		m.put(c, Color.CYAN);
		m.put(d, Color.YELLOW);
		m.put(e, Color.BLACK);
		m.put(f, Color.GRAY);
		m.put(this.g, Color.RED);
		m.put(h, Color.BLUE);
		m.put(i, Color.CYAN);
		m.put(j, Color.YELLOW);
		m.put(k, Color.BLACK);
		for( double x = 0; x < 600; x++)
		{
			for (double y = 0; y < 630; y++)
			{
				Node n = nearestNode(x,y);
				Color z =  m.get( n ) ;
				g.setColor( new Color(z.getRed(), z.getGreen(), z.getBlue(), 100));
				g.drawLine( (int)x, (int)y, (int)x, (int)y );
			}
		}
		
		*/
	}
	
	public void keyTyped(KeyEvent e)
	{
	}
	public void keyReleased(KeyEvent e)
	{
		if(e.getKeyCode()==KeyEvent.VK_UP) up=false;
		if(e.getKeyCode()==KeyEvent.VK_DOWN) down=false;
		if(e.getKeyCode()==KeyEvent.VK_LEFT) left=false;
		if(e.getKeyCode()==KeyEvent.VK_RIGHT) right=false;
		if(e.getKeyCode()==KeyEvent.VK_SPACE) fire=false;
	}
	public void keyPressed(KeyEvent e)
	{
		if(e.getKeyCode()==KeyEvent.VK_UP) up=true;
		if(e.getKeyCode()==KeyEvent.VK_DOWN) down=true;
		if(e.getKeyCode()==KeyEvent.VK_LEFT) left=true;
		if(e.getKeyCode()==KeyEvent.VK_RIGHT) right=true;
		if(e.getKeyCode()==KeyEvent.VK_SPACE) fire=true;
	}
	
	public static void main(String[] args){
		new Game();
	}
	public static Image imageFromFile(String filename)
	{
		ImageIcon i = new ImageIcon(filename);
		if (i.getIconHeight() == -1 ) throw new RuntimeException( "Can not load image! >:O " + filename );
		return i.getImage();
	}
}